#pragma once
#ifndef ObjComp_h__
#define ObjComp_h__

#include "Common.h"
#include "Geo.h"
#include "Gfx.h"
#include "Particles.h"

#include <typeinfo>
#include <eastl/vector.h>
#include <eastl/map.h>

enum ComponentPoolFlags
{
	CPF_Grow = 1,
};

template <typename T, size_t GRANULARITY = 256, int FLAGS = CPF_Grow>
struct ComponentPool
{
	T DefaultPool[GRANULARITY];
	eastl::vector<T*> Pools;
	eastl::vector<T*> FreeComponents;

	ComponentPool()
	{
		AddPool(DefaultPool);
	}

	~ComponentPool()
	{
		for (size_t i=1; i<Pools.size(); i++)
			delete[] Pools[i];
	}

	void AddPool(T* pool)
	{
		for (size_t i=0; i<GRANULARITY; i++)
			FreeComponents.push_back(&pool[i]);
	}

	T* Alloc()
	{
		if (FreeComponents.empty())
		{
			if (FLAGS & CPF_Grow)
			{
				AddPool(new T[GRANULARITY]);
			}
			else
				return 0;
		}

		T* comp = FreeComponents.back();
		FreeComponents.pop_back();
		comp->Reset();
		return comp;
	}

	void Free(T* obj)
	{
#ifdef DEBUG
		bool in_any = false;
		for (size_t i=0; i<Pools.size(); i++)
		{
			if (obj >= Pools[i] && obj < Pools[i]+GRANULARITY)
			{
				in_any = true;
				break;
			}
		}
		assert(in_any);
#endif
		FreeComponents.push_back(obj);
	}
};

struct Entity;

struct BaseComponent
{
	virtual void Update(float dt) {}
	virtual void Render(const Vec& offset) {}
	virtual void Reset() {}
	virtual ~BaseComponent() {}

	BaseComponent* NextSibling;
	Entity* Owner;
};

template <typename T>
struct Component : public BaseComponent
{
	static ComponentPool<T>& GetPool() {
		static ComponentPool<T> pool;
		return pool;
	}
};

struct Dynamics : public Component<Dynamics>
{
	Vec Vel;
	float Friction;

	virtual void Reset()
	{
		Vel = Vec(0,0);
		Friction = 0.0f;
	}

	virtual void Update(float dt);
};

struct Dimensions : public Component<Dimensions>
{
	Vec Size;
	float Radius;
	bool Circular;

	virtual void Reset()
	{
		Size = Vec(0,0);
		Radius = 1.0f;
		Circular = true;
	}

	Vec GetCenter();
	bool IntersectsSquare(const Entity& other);
	bool IntersectPoint(const Vec& pt);
	bool IntersectsRadius(const Entity& other);
};

struct Rotation : Component<Rotation>
{
	float Rot;
	float RotSpeed;

	virtual void Reset()
	{
		Rot = 0.0f;
		RotSpeed = 0.0f;
	}
};

struct Collision : Component<Collision>
{
	int CollisionGroup;
	static const int NO_COLLISION = 0;

	virtual void Reset()
	{
		CollisionGroup = NO_COLLISION;
	}
};

struct Graphics : Component<Graphics>
{
	Pic Img;
	/// Bitmask
	Color BlendColor;
	uint32_t RenderInPasses;

	virtual void Reset()
	{
		Img = 0;
		RenderInPasses = 0x1;
		BlendColor = Color::White();
	}

	virtual void Render(const Vec& offset);
};

struct Lifetime : Component<Lifetime>
{
	double Life;

	virtual void Reset()
	{
		Life = 0;
	}
};

struct Healthy : Component<Healthy>
{
	float BaseHealth;
	float Health;
	void Damage(float hp, int damage_type, Entity* source) { }

	virtual void Reset()
	{
		BaseHealth = 100.0f;
		Health = 100.0f;
	}
};

struct Timers : Component<Timers>
{
	eastl::map<int, double> AllTimers;
	double GetTimer(int timer);
	void SetTimer(int timer, double time);

	virtual void Reset()
	{
		AllTimers.clear();
	}
};

struct ParticleEmitter : Component<ParticleEmitter>
{
	Part Definition;
	float PPS;
	
	virtual void Reset()
	{
		Definition = Part();
		PPS = 0;
		mAccum = 0;
	}

	virtual void Update(float dt);

protected:

	float mAccum;
};

struct Entity
{
	Vec Pos;
	//World* Wrld;
	bool Dead;

	Entity() : Pos(0,0), Dead(false), Components(0) {}
	virtual ~Entity() {

	}

	virtual int Type() { return 'Enti'; }

	virtual void Update(float dt)
	{
		BaseComponent* comp = Components;
		while (comp)
		{
			comp->Update(dt);
			comp = comp->NextSibling;
		}
	}

	virtual void Render(const Vec& offset)
	{
		BaseComponent* comp = Components;
		while (comp)
		{
			comp->Render(offset);
			comp = comp->NextSibling;
		}
	}

	//virtual void Event(int event, const Prop& arg);

	template <typename T>
	T* GetComponent() const
	{
		const std::type_info& search = typeid(T);
		BaseComponent* comp = Components;
		while (comp)
		{
			if (typeid(*comp) == search)
				return (T*)comp;
			comp = comp->NextSibling;
		}
		return 0;
	}


	template <typename T>
	T* AddComponent() 
	{
		T* comp = Component<T>::GetPool().Alloc();
		comp->Owner = this;
		comp->NextSibling = Components;
		Components = comp;
		return comp;
	}

	BaseComponent* Components;
};

template <typename T>
struct ComponentPtr
{
	T* Ptr;
	ComponentPtr(Entity* owner) : Ptr(owner->AddComponent<T>()) { }
	T* operator->() { return Ptr; }
	const T* operator->() const { return Ptr; }
};

#endif